﻿namespace Microsoft.SharePoint.Client
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Threading;
    using System.Xml;

    public class ClientRequest
    {
        internal const int GenericErrorCode = -1;
        internal const long InvalidId = -1L;
        private List<ClientObject> m_clientObjectCleanupList;
        private List<ObjectPath> m_clientObjectPathCleanupList;
        private ClientRuntimeContext m_context;
        private Stack<ExecutionScope> m_executionScopes;
        private ClientAction m_lastAction;
        private List<ClientAction> m_queries = new List<ClientAction>();
        private Dictionary<string, object> m_queryIdToObjectMap = new Dictionary<string, object>();
        private HttpWebRequest m_request;
        private ClientRequestStatus m_requestStatus;
        private Microsoft.SharePoint.Client.SerializationContext m_serializationContext;
        private string m_soapPageUrl;
        private static long s_sequenceId;

        internal ClientRequest(ClientRuntimeContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            this.m_context = context;
        }

        internal void AddObjectToQueryCleanupList(ClientObject obj)
        {
            if (this.m_clientObjectCleanupList == null)
            {
                this.m_clientObjectCleanupList = new List<ClientObject>();
            }
            this.m_clientObjectCleanupList.Add(obj);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        internal void AddQuery(ClientAction query)
        {
            if (this.m_requestStatus != ClientRequestStatus.Active)
            {
                throw new InvalidOperationException(Resources.GetString("RequestHasBeenExecuted"));
            }
            this.m_queries.Add(query);
            this.m_lastAction = query;
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        internal void AddQueryIdAndResultObject(long id, object obj)
        {
            if (this.m_requestStatus != ClientRequestStatus.Active)
            {
                throw new InvalidOperationException(Resources.GetString("RequestHasBeenExecuted"));
            }
            this.m_queryIdToObjectMap[id.ToString(CultureInfo.InvariantCulture)] = obj;
            if ((obj is ClientObject) && (((ClientObject) obj).ObjectData.AssociatedObject != null))
            {
                this.m_queryIdToObjectMap[id.ToString()] = ((ClientObject) obj).ObjectData.AssociatedObject;
            }
        }

        internal void AddToObjectPathCleanupList(ObjectPath path)
        {
            if (this.m_clientObjectPathCleanupList == null)
            {
                this.m_clientObjectPathCleanupList = new List<ObjectPath>();
            }
            this.m_clientObjectPathCleanupList.Add(path);
        }

        private ChunkStringBuilder BuildQuery()
        {
            Microsoft.SharePoint.Client.SerializationContext serializationContext = this.SerializationContext;
            ChunkStringBuilder builder = new ChunkStringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings {
                OmitXmlDeclaration = true,
                NewLineHandling = NewLineHandling.Entitize
            };
            XmlWriter writer = XmlWriter.Create(builder.CreateTextWriter(CultureInfo.InvariantCulture), settings);
            writer.WriteStartElement("Request", "http://schemas.microsoft.com/sharepoint/clientquery/2009");
            writer.WriteAttributeString("AddExpandoFieldTypeSuffix", "true");
            writer.WriteAttributeString("SchemaVersion", ClientSchemaVersions.CurrentVersion.ToString());
            writer.WriteAttributeString("LibraryVersion", "14.0.4730.1010");
            if (!string.IsNullOrEmpty(this.m_context.ApplicationName))
            {
                writer.WriteAttributeString("ApplicationName", this.m_context.ApplicationName);
            }
            writer.WriteStartElement("Actions");
            Stack<ExecutionScope> stack = new Stack<ExecutionScope>();
            foreach (ClientAction action in this.m_queries)
            {
                if (action is ClientActionExecutionScopeStart)
                {
                    ClientActionExecutionScopeStart start = (ClientActionExecutionScopeStart) action;
                    start.Scope.WriteStart(writer, serializationContext);
                    stack.Push(start.Scope);
                    continue;
                }
                if (action is ClientActionExecutionScopeEnd)
                {
                    ClientActionExecutionScopeEnd end = (ClientActionExecutionScopeEnd) action;
                    if ((stack.Count == 0) || (stack.Pop() != end.Scope))
                    {
                        throw ExceptionHandlingScope.CreateInvalidUsageException();
                    }
                    end.Scope.WriteEnd(writer, serializationContext);
                    continue;
                }
                action.WriteToXml(writer, serializationContext);
            }
            if (stack.Count > 0)
            {
                throw ExceptionHandlingScope.CreateInvalidUsageException();
            }
            writer.WriteEndElement();
            writer.WriteStartElement("ObjectPaths");
            Dictionary<long, ObjectPath> dictionary = new Dictionary<long, ObjectPath>();
            while (true)
            {
                List<long> list = new List<long>();
                foreach (long num in serializationContext.Paths.Keys)
                {
                    if (!dictionary.ContainsKey(num))
                    {
                        list.Add(num);
                    }
                }
                if (list.Count == 0)
                {
                    break;
                }
                for (int i = 0; i < list.Count; i++)
                {
                    ObjectPath path = this.m_context.ObjectPaths[list[i]];
                    path.WriteToXml(writer, serializationContext);
                    dictionary[list[i]] = path;
                }
            }
            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.Flush();
            return builder;
        }

        private void CleanupQuery()
        {
            if (this.m_clientObjectCleanupList != null)
            {
                foreach (ClientObject obj2 in this.m_clientObjectCleanupList)
                {
                    obj2.CleanupQuery();
                }
                this.m_clientObjectCleanupList = null;
            }
            if (this.m_clientObjectPathCleanupList != null)
            {
                foreach (ObjectPath path in this.m_clientObjectPathCleanupList)
                {
                    path.Invalidate();
                }
                this.m_clientObjectPathCleanupList = null;
            }
        }

        internal void ExecuteQuery(ClientRequestSucceededEventHandler succeededCallback, ClientRequestFailedEventHandler failedCallback)
        {
            if (this.m_requestStatus != ClientRequestStatus.Active)
            {
                throw new ClientRequestException(Resources.GetString("RequestHasBeenExecuted"));
            }
            this.m_requestStatus = ClientRequestStatus.InProgress;
            ChunkStringBuilder sb = this.BuildQuery();
            this.CleanupQuery();
            this.ExecuteQueryToServer(sb, succeededCallback, failedCallback);
        }

        private void ExecuteQueryGetRequestStreamAsyncCallback(IAsyncResult asyncResult)
        {
            ClientQueryAsyncState asyncState = (ClientQueryAsyncState) asyncResult.AsyncState;
            try
            {
                using (Stream stream = asyncState.WebRequest.EndGetRequestStream(asyncResult))
                {
                    asyncState.Query.WriteContentAsUTF8(stream);
                }
                asyncState.WebRequest.BeginGetResponse(new AsyncCallback(this.ExecuteQueryGetResponseAsyncCallback), asyncState);
            }
            catch (Exception exception)
            {
                if (asyncState.FailedCallback != null)
                {
                    asyncState.FailedCallback(this, new ClientRequestFailedEventArgs(this, exception));
                }
            }
        }

        private void ExecuteQueryGetResponseAsyncCallback(IAsyncResult asyncResult)
        {
            Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture;
            ClientQueryAsyncState asyncState = (ClientQueryAsyncState)asyncResult.AsyncState;
            bool flag = false;
            try
            {
                HttpWebResponse response = (HttpWebResponse) asyncState.WebRequest.EndGetResponse(asyncResult);
                this.ProcessResponse(response);
                flag = this.RequestStatus == ClientRequestStatus.CompletedSuccess;
            }
            catch (Exception exception)
            {
                if (asyncState.FailedCallback != null)
                {
                    asyncState.FailedCallback(this, new ClientRequestFailedEventArgs(this, exception));
                }
            }
            if (flag && (asyncState.SucceededCallback != null))
            {
                asyncState.SucceededCallback(this, new ClientRequestSucceededEventArgs(this));
            }
        }

        private void ExecuteQueryToServer(ChunkStringBuilder sb, ClientRequestSucceededEventHandler succeededCallback, ClientRequestFailedEventHandler failedCallback)
        {
            this.m_context.FireExecutingWebRequestEvent(new WebRequestEventArgs(this.WebRequest));
            ClientQueryAsyncState state = new ClientQueryAsyncState(sb, this.WebRequest, succeededCallback, failedCallback);
            try
            {
                this.WebRequest.BeginGetRequestStream(new AsyncCallback(this.ExecuteQueryGetRequestStreamAsyncCallback), state);
            }
            catch (Exception exception)
            {
                if (failedCallback != null)
                {
                    failedCallback(this, new ClientRequestFailedEventArgs(this, exception));
                }
            }
        }

        private void ProcessResponse(HttpWebResponse response)
        {
            if (response.StatusCode != HttpStatusCode.OK)
            {
                this.m_requestStatus = ClientRequestStatus.CompletedException;
                throw new ClientRequestException(Resources.GetString("RequestUnexpectedResponse", new object[] { response.ContentType, response.StatusCode }));
            }
            this.m_context.ProcessingResponse = true;
            try
            {
                using (Stream stream = response.GetResponseStream())
                {
                    this.ProcessResponseStream(stream);
                    this.m_requestStatus = ClientRequestStatus.CompletedSuccess;
                }
            }
            catch (Exception)
            {
                this.m_requestStatus = ClientRequestStatus.CompletedException;
                throw;
            }
            finally
            {
                this.m_context.ProcessingResponse = false;
            }
        }

        private void ProcessResponseStream(Stream responseStream)
        {
            object obj2;
            StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
            JsonReader reader2 = new JsonReader(reader, this.m_context);
            reader2.ReadArrayStart();
            Dictionary<string, object> dictionary = reader2.ReadObject() as Dictionary<string, object>;
            if (dictionary == null)
            {
                throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
            }
            if (!dictionary.TryGetValue("SchemaVersion", out obj2))
            {
                throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
            }
            string str = obj2 as string;
            if (string.IsNullOrEmpty(str))
            {
                throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
            }
            this.m_context.ServerSchemaVersion = new Version(str);
            if (!dictionary.TryGetValue("LibraryVersion", out obj2))
            {
                throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
            }
            string str2 = obj2 as string;
            if (string.IsNullOrEmpty(str2))
            {
                throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
            }
            this.m_context.ServerLibraryVersion = new Version(str2);
            if (!dictionary.TryGetValue("ErrorInfo", out obj2))
            {
                throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
            }
            object obj3 = obj2;
            if (obj3 != null)
            {
                Dictionary<string, object> errorInfo = obj3 as Dictionary<string, object>;
                if (errorInfo == null)
                {
                    throw new ClientRequestException(Resources.GetString("RequestUnknownResponse"));
                }
                throw ServerException.CreateFromErrorInfo(errorInfo);
            }
            if (!ClientRuntimeContext.CanHandleResponseSchema(this.m_context.ServerSchemaVersion))
            {
                throw new ClientRequestException(Resources.GetString("CannotHandleServerResponseSchema", new object[] { str }));
            }
            while (reader2.PeekTokenType() != JsonTokenType.ArrayEnd)
            {
                long num = reader2.ReadInt64();
                obj2 = null;
                if (this.m_queryIdToObjectMap.TryGetValue(num.ToString(CultureInfo.InvariantCulture), out obj2) && (obj2 != null))
                {
                    IFromJson json = obj2 as IFromJson;
                    if ((json != null) && !json.CustomFromJson(reader2))
                    {
                        json.FromJson(reader2);
                    }
                }
                else
                {
                    reader2.ReadObject();
                }
            }
        }

        internal List<ClientAction> Actions
        {
            get
            {
                return this.m_queries;
            }
        }

        internal ClientRuntimeContext Context
        {
            get
            {
                return this.m_context;
            }
        }

        internal Stack<ExecutionScope> ExecutionScopes
        {
            get
            {
                if (this.m_executionScopes == null)
                {
                    this.m_executionScopes = new Stack<ExecutionScope>();
                }
                return this.m_executionScopes;
            }
        }

        internal ClientAction LastAction
        {
            get
            {
                return this.m_lastAction;
            }
        }

        public static long NextSequenceId
        {
            get
            {
                return Interlocked.Increment(ref s_sequenceId);
            }
        }

        internal Dictionary<string, object> QueryIdToObjectMap
        {
            get
            {
                return this.m_queryIdToObjectMap;
            }
        }

        internal ClientRequestStatus RequestStatus
        {
            get
            {
                return this.m_requestStatus;
            }
        }

        internal Microsoft.SharePoint.Client.SerializationContext SerializationContext
        {
            get
            {
                if (this.m_serializationContext == null)
                {
                    this.m_serializationContext = new Microsoft.SharePoint.Client.SerializationContext();
                }
                return this.m_serializationContext;
            }
        }

        private string SoapPageUrl
        {
            get
            {
                if (string.IsNullOrEmpty(this.m_soapPageUrl))
                {
                    this.m_soapPageUrl = this.m_context.Url;
                    if (!this.m_soapPageUrl.EndsWith("/"))
                    {
                        this.m_soapPageUrl = this.m_soapPageUrl + "/_vti_bin/client.svc/ProcessQuery";
                    }
                    else
                    {
                        this.m_soapPageUrl = this.m_soapPageUrl + "_vti_bin/client.svc/ProcessQuery";
                    }
                    if ((ApplicationContext.Current != null) && (string.Compare(ApplicationContext.Current.Url, this.m_context.Url, StringComparison.OrdinalIgnoreCase) == 0))
                    {
                        this.m_soapPageUrl = ApplicationContext.Current.GetRequestUrl(this.m_soapPageUrl);
                    }
                }
                return this.m_soapPageUrl;
            }
        }

        public HttpWebRequest WebRequest
        {
            get
            {
                if (this.m_request == null)
                {
                    this.m_request = (HttpWebRequest) System.Net.WebRequest.Create(new Uri(this.SoapPageUrl));
                    this.m_request.Method = "POST";
                    this.m_request.ContentType = "text/xml";
                    if ((ApplicationContext.Current != null) && !string.IsNullOrEmpty(ApplicationContext.Current.RequestToken))
                    {
                        this.m_request.Headers["X-RequestToken"] = ApplicationContext.Current.RequestToken;
                    }
                }
                return this.m_request;
            }
        }

        private class ClientQueryAsyncState
        {
            private ClientRequestFailedEventHandler m_failedCallback;
            private ChunkStringBuilder m_query;
            private ClientRequestSucceededEventHandler m_succeededCallback;
            private HttpWebRequest m_webRequest;

            public ClientQueryAsyncState(ChunkStringBuilder query, HttpWebRequest webRequest, ClientRequestSucceededEventHandler succeededCallback, ClientRequestFailedEventHandler failedCallback)
            {
                this.m_query = query;
                this.m_webRequest = webRequest;
                this.m_succeededCallback = succeededCallback;
                this.m_failedCallback = failedCallback;
            }

            public ClientRequestFailedEventHandler FailedCallback
            {
                get
                {
                    return this.m_failedCallback;
                }
            }

            public ChunkStringBuilder Query
            {
                get
                {
                    return this.m_query;
                }
            }

            public ClientRequestSucceededEventHandler SucceededCallback
            {
                get
                {
                    return this.m_succeededCallback;
                }
            }

            public HttpWebRequest WebRequest
            {
                get
                {
                    return this.m_webRequest;
                }
            }
        }
    }
}

